#include <iostream>
using namespace std;
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

// void hander(int signo) //自定义捕捉动作
// {
//     cout<<"程序异常，错误码:"<<signo<<endl;
//     sleep(1);
// }

// int main()
// {
//     //对于这些信号，系统默认是终止，如果我们自定义动作没有终止则进程始终会报错 - 因为没有处理信号，也没有能力处理信号

//     signal(SIGSEGV,hander); 

//     int* p = nullptr;
//     // p = (int*)100;
//     *p = 100; //野指针问题(悬垂指针) - 11号信号错误

//     signal(SIGFPE,hander);

//     int a = 10;
//     a /= 0; //除0导致Floating point exception错误会发送8号信号SIGFPE

//     //当a/=0和*p=100指令被导入CPU寄存器进行处理时，会出现错误，CPU中状态寄存器会记录错误将位图对应比特位置为1，此时是硬件异常
//     //因为操作系统是管理软硬件的，操作系统识别到对应的异常(非法状态)，操作系统通过寄存器(寄存器中存放对应PCB地址)找到进程向进程PCB中写入信号
//     //除0的本质就是触发硬件异常(硬件就是CPU)
//     //如果是自定义捕捉，当系统执行了捕捉动作后回到程序，由于信号没有被处理，所以操作系统会一直给进程发信号，捕捉动作会一直执行

//     return 0;
// }



//研究core dump位0/1的条件
// int main()
// {
//     pid_t id = fork();
//     if(id == 0)
//     {
//         int* p = nullptr;
//         *p = 100;
//         exit(2);
//     }
//     int status = 0;
//     wait(&status);
//     cout<<"退出码: "<<((status>>8) & 0xFF) <<endl;
//     cout<<"信号: "<<((status) & 0x7F) <<endl;
//     cout<<"core dump: "<<((status>>7) & 1) <<endl;

//     //core dump核心转储文件只有被实际产生，core dump位才会置为1
//     //否则无论是core还是term产生信号core dump位才会置为0
//     //所以在core dump功能没有打开时，是不会标记core dump位的
//     return 0;
// }




// #define SIG_ERR	((__sighandler_t) -1)		/* Error return.   */ 返回错误
// #define SIG_DFL	((__sighandler_t) 0)		/* Default action. */ 默认动作
// #define SIG_IGN	((__sighandler_t) 1)		/* Ignore signal.  */ 忽略信号
//这三个信号宏是通过整型被转换成函数指针传入的，在处理时会先进行判断
//typedef void (*__sighandler_t) (int); 函数指针类型
// int main()
// {
//     //signal(2,SIG_DFL); //SIG_DFL默认处理动作
//     //signal(2,SIG_IGN); //SIG_IGN忽略该信号
//     //signal(2,SIG_ERR); //SIG_ERR错误返回

//     while(true);
//     return 0;
// }


//sigset_t 模型 - 默认1024个比特位的位图
// # define _SIGSET_NWORDS	(1024 / (8 * sizeof (unsigned long int))) - 一个整数只有32个比特位(long long是64个) 未来如果我们需要1000个比特位，一个整数肯定是不够的，所以系统开辟了一个数组，这个数组中有1024/(8*4)=32个32位图结构的数组
// typedef struct
//   {
//     unsigned long int __val[_SIGSET_NWORDS]; // 数组下标计算时，例如500，500/32得到下标15，然后-1在14数组中500%32得到位图位置，将该位置1左移对应的位数，最后按位与上该数组数就能将位图写入
//   } __sigset_t; // __val[14] & 1<<(500/32)

// void print(const sigset_t& set)
// {
//     for(int i = 1;i<32;++i)
//     {
//         if(sigismember(&set,i)) 
//         {
//             cout<<1;
//         }
//         else
//         {
//             cout<<0;
//         }
//     }
//     cout<<endl;
// }

void hander(int signo)
{
    cout<<"信号: "<<signo<<endl;
    exit(1);
}

// //sigset_t 学习
// int main()
// {
//     sigset_t set;
//     sigset_t oset;

//     sigemptyset(&set); //初始化信号集
//     sigemptyset(&oset);
//     sigaddset(&set,2);//屏蔽二号信号CTRL+C
//     sigprocmask(SIG_SETMASK,&set,&oset); //将信号集设置进入block阻塞表

//     int cnt = 1;
//     while(true)
//     {
//         //打印当前设置的阻塞信号集
//         print(oset); //设置之后CTRL+C失效
//         sleep(1);

//         if(cnt == 5)
//         {
//             cout<<endl;
//             signal(2,hander);
//             print(set);
//             print(oset);
//             sigprocmask(SIG_SETMASK,&oset,&set); //五次后信号集恢复
//             print(set);
//             print(oset);
//             break;
//         }
//         else
//         {
//             ++cnt;
//         }
//     }
//     return 0;
// }


//验证子进程是否会继承修改的signal信号
int main()
{
    pid_t id = fork();
    signal(2,hander);
    if(id == 0)
    {
        while(true)
        {
            cout<<"子进程运行中: "<<getpid()<<endl;
            sleep(1);
        }
    }

    signal(2,SIG_DFL); //父进程恢复为默认
    wait(nullptr);
    return 0;
} //结论：显然子进程会复制父进程的信号表，此后父子进程进行修改发生写时拷贝，父子进程修改不影响对方